package com.rankeiot.core.excel;

import ch.rabanti.nanoxlsx4j.styles.Border;
import ch.rabanti.nanoxlsx4j.styles.CellXf;
import ch.rabanti.nanoxlsx4j.styles.Style;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.rankeiot.core.anno.Excel;
import com.rankeiot.core.data.Item;
import com.rankeiot.core.translator.FormatTranslator;
import com.rankeiot.core.translator.Translator;
import com.rankeiot.core.util.IDUtil;
import com.rankeiot.core.util.StringUtil;
import lombok.extern.java.Log;

import org.sagacity.sqltoy.callback.StreamResultHandler;
import org.sagacity.sqltoy.utils.BeanUtil;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.*;

/**
 * 流式导出Excel数据
 */
@Log
public class ExcelStreamExporter implements StreamResultHandler {
    private final int max_row = 1048576;
    private final XlsxStreamWriter writer;
    private XlsxStreamWriter.SheetWriter sw;
    private final Map<String, Translator> translators;

    private Map<Integer, Translator> colTranslators;

    private String[] columnsLabels;

    private boolean isListExport = false;

    //获得导出字段及表头
    private List<Item> titles = new ArrayList<>();

    private boolean dataIsMap = false;
    private long startTime;
    private long total;

    private Style defaultStyle = null;
    private Style headStyle = null;

    public ExcelStreamExporter(OutputStream out) {
        this(out, new HashMap<>());
    }

    public ExcelStreamExporter(OutputStream out, Map<String, Translator> translators) {
        writer = new XlsxStreamWriter(out);
        this.translators = translators;
        try {
            sw = writer.createSheet("sheet1");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        //头部样式
        Style headStyle = createStyle();
        headStyle.getFont().setBold(true);
        headStyle.getCellXf().setHorizontalAlign(CellXf.HorizontalAlignValue.center);
        this.setHeadStyle(headStyle);
        //普通表格样式
        this.setDefaultStyle(createStyle());
    }
    public void translate(String field,Translator translator){
        translators.put(field,translator);
    }
    protected static Style createStyle() {
        Style style = new Style();

        String defaultColor = "F0000000";

        Border border = style.getBorder();
        border.setRightColor(defaultColor);
        border.setRightStyle(Border.StyleValue.thin);

        border.setLeftColor(defaultColor);
        border.setLeftStyle(Border.StyleValue.thin);

        border.setTopColor(defaultColor);
        border.setTopStyle(Border.StyleValue.thin);

        border.setBottomColor(defaultColor);
        border.setBottomStyle(Border.StyleValue.thin);

        style.getCellXf().setAlignment(CellXf.TextBreakValue.wrapText);
        style.getCellXf().setVerticalAlign(CellXf.VerticalAlignValue.center);

        return style;
    }

    public void start(String[] columnsLabels, String[] columnsTypes) {
        startTime = System.currentTimeMillis();
        this.columnsLabels = columnsLabels;
        colTranslators = new HashMap<>();
        for (int i = 0; i < columnsLabels.length; i++) {
            String col = columnsLabels[i];
            Translator translator = translators.get(col);
            if (translator != null) {
                colTranslators.put(i, translator);
            }
        }
    }

    @Override
    public void consume(Object row, int rowIndex) {
        try {
            total = rowIndex;
            onRow(row, rowIndex);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    protected void onRow(Object row, int rowIndex) throws IOException {
        if (rowIndex == 0) {
            if (row instanceof List) {
                isListExport = true;
                sw.addRow(headStyle, columnsLabels);
            } else {
                if (row instanceof Map) {
                    dataIsMap = true;
                }
                //创建转换器和columnsLabels
                init(row.getClass());
                sw.addRow(headStyle, columnsLabels);
            }
        }
        if (rowIndex > 0 && rowIndex % max_row == 0) {
            int index = rowIndex / max_row + 1;
            sw = writer.createSheet("sheet" + index);
            sw.addRow(headStyle, columnsLabels);
        }
        if (isListExport) {
            List li = (List) row;
            if (colTranslators.size() > 0) {
                for (Map.Entry<Integer, Translator> e : colTranslators.entrySet()) {
                    int i = e.getKey();
                    Object v = li.get(i);
                    v = e.getValue().translate(li, columnsLabels[i], v);
                    li.set(i, v);
                }
            }
            sw.addRow(defaultStyle, li);
        } else {
            sw.addRow(defaultStyle, toRow(row));
        }
    }

    protected void init(Class exportDef) {

        Set<String> fields = new HashSet<>();
        for (Field field : exportDef.getDeclaredFields()) {
            Excel export = field.getAnnotation(Excel.class);
            if (export == null) {
                continue;
            }
            if (!export.out()) {
                fields.add(field.getName());
                continue;
            }
            Item title = new Item();
            title.setKey(field.getName());
            title.setTitle(field.getName());
            if (StringUtil.isNotEmpty(export.value())) {
                title.setTitle(export.value());
            }
            title.setOrder(export.order());
            titles.add(title);
            fields.add(field.getName());
            //使用本身translate,不再额外处理,只处format理格式转换
            if (!translators.containsKey(title.getKey())) {
                Translator translator = null;
                JsonFormat jf = field.getAnnotation(JsonFormat.class);
                if (jf != null && StringUtil.isNotEmpty(jf.pattern())) {
                    translator = new FormatTranslator(jf.pattern());
                    translators.put(title.getKey(), translator);
                }
            }
        }
        //父级,最多处理两级
        for (Field field : exportDef.getSuperclass().getDeclaredFields()) {
            Excel export = field.getAnnotation(Excel.class);
            if (export == null) {
                continue;
            }
            if (!export.out()) {
                continue;
            }
            if (fields.contains(field.getName())) {
                continue;
            }
            Item title = new Item();
            title.setKey(field.getName());
            title.setTitle(field.getName());
            if (StringUtil.isNotEmpty(export.value())) {
                title.setTitle(export.value());
            }
            title.setOrder(export.order());
            titles.add(title);
            if (!translators.containsKey(title.getKey())) {
                Translator translator = null;
                JsonFormat jf = field.getAnnotation(JsonFormat.class);
                if (jf != null && StringUtil.isNotEmpty(jf.pattern())) {
                    translator = new FormatTranslator(jf.pattern());
                    translators.put(title.getKey(), translator);
                }
            }
        }
        Collections.sort(titles);
        columnsLabels = new String[titles.size()];
        for (int i = 0; i < titles.size(); i++) {
            columnsLabels[i] = titles.get(i).getTitle();
        }
    }

    protected List toRow(Object data) {
        List result = new ArrayList();
        for (Item title : titles) {
            Object value = null;
            if (dataIsMap) {
                value = ((Map) data).get(title.getKey());
            } else {
                try {
                    value = BeanUtil.getProperty(data, title.getKey());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if (value == null) {
                result.add("");
            } else {
                Translator translator = translators.get(title.getKey());
                if (translator != null) {
                    Object real = translator.translate(data, title.getKey(), value);
                    result.add(real);
                } else {
                    result.add(value);
                }
            }
        }
        return result;
    }

    @Override
    public void end() {
        try {
            writer.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            long cost = (System.currentTimeMillis() - startTime) / 1000;
            System.out.println("导出数据 " + total + " 条，耗时 " + cost + " s ");
            log.info("导出数据 " + total + " 条，耗时 " + cost + " s ");
        }
    }

    public Style getDefaultStyle() {
        return defaultStyle;
    }

    public void setDefaultStyle(Style defaultStyle) {
        this.defaultStyle = defaultStyle;
        if (defaultStyle != null)
            writer.getStyles().addStyle(defaultStyle);
    }

    public Style getHeadStyle() {
        return headStyle;
    }

    public void setHeadStyle(Style headStyle) {
        this.headStyle = headStyle;
        if (headStyle != null)
            writer.getStyles().addStyle(headStyle);
    }
}
